← C++ 高级程序设计(2020)2020-C++高级程序设计-C++ 异常处理
约 17 分钟阅读阅读 — 次
C++ 异常处理
- 错误
- 语法错误:编译系统
- 逻辑错误:测试
- 异常 Exception
- 运行环境造成:内存不足、文件操作失败等
- 异常处理:错误提示信息等
1. 异常处理
- 特征:
- 可以预见
- 无法避免
- 作用:提高程序鲁棒性(Bobustness)
1 2 3 4 5 6 7 8
| void f(char *str) { ifstream file(str); if (file.fail()) { } int x; file >> x; }
|
- 问题:发现异常之处与处理异常之处不一致,怎么处理?函数中的异常要告知调用者
- 常见处理方式:
- 函数参数:
- 返回值(特殊的,0或者1)
- 引用参数(存放一些特定的信息)
- 逐层返回
- 缺陷:程序结构不清楚
- 相同的异常,不同的地方,需要编写相同的处理了逻辑是不合理的
- 传统异常处理方式不能处理构造函数出现的异常
1.1. 处理机制
- C++异常处理机制是,一种专门、清晰描述异常处理过程的机制
- try:监控
- throw:抛掷异常对象,不处理
- catch:捕获并处理
1 2 3 4 5 6 7 8
| try{ }throw catch(<类型>[<变量>]){ }
|
1.2. catch的用法
- 类型:异常类型,匹配规则同函数重载(精确匹配只有底下三种,int转double都不行)
- 允许从非常量到常量转换
- 允许从派生类到基类转换
- 允许数组和函数转换成指针
- 变量:存储异常对象,可省
- 一个try语句块的后面可以跟多个catch语句块,用于捕获不同类型的异常进行处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void f() { throw 1; throw 1.0; throw "abcd"; } try { f(); }catch (int) {...} catch(double) {...} catch(char *)
{...}
|
1.3. 异常处理的嵌套
- 调用关系:f->g->h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| f(){ try{ g(); }catch (int) { … } catch (char *) { … } } g(){ try{ h(); }catch (int) { … } } h(){ throw 1; throw "abcd"; }
|
- 如果所抛掷的异常对象如果在调用链上未被捕获,则由系统的abort处理,尽量不要
1.4. 定义异常类
- 注意catch块排列顺序:这样子保证了继承顺序(重要),顺序向下检查是否符合条件,一旦符合条件就不再向下查找了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class FileErrors { }; class NonExist:public FileErrors { } ; class WrongFormat:public FileErrors { } ; class DiskSeekError:public FileErrors { };
int f(){ try{ WrongFormat wf; throw wf; }catch(NonExists&){...} catch(DiskSeekError&){...} catch(FileErrors){...} } int f(){ try{ WrongFormat wf; throw wf; }catch(FileErrors){...} catch(NonExists&){...} catch(DiskSeekError&){...} }
|
- 实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class MyExceptionBase {}; class MyExceptionDerived: public MyExceptionBase { }; void f(MyExceptionBase& e) { throw e; } int main() { MyExceptionDerived e; try { f(e); }catch(MyExceptionDerived& e) { cout << "MyExceptionDerived" << endl; }catch(MyExceptionBase& e) { cout << "MyExceptionBase" << endl; } }
|
1.5. 异常处理的特例
- 无参数 throw:将捕获到的异常对象重新抛掷出去
catch(int){throw;}
- catch(…):默认异常处理,这三个点是标准语法,捕获所有异常
- 实现:不影响对象布局:程序状态<->析构函数、异常处理器,对程序验证特征的支持
- 构造函数的初始化表前,放置try-catch同样捕获异常1
1 2 3 4 5 6 7
| template<class T, class E> inline void Assert(T exp, E e) { if (DEBUG) if (!exp) throw e; }
|
- 问题:如何应对多出口引发的处理碎片问题,如果多个地方throw,则意味着这里有多个出口。
- Java中在异常处理这一部分提供了Finally操作,无论在哪里没有抛出最后都会执行finally,将内存缓存进行自己的处理
- 可是C中没有finally,那怎么进行处理呢?这个在C中,执行完异常处理后,必然执行析构函数
1 2 3 4 5 6 7 8 9 10 11
| class Empty { }; class Empty { Empty(); Empty(const Empty&); ~Empty(); Empty& operator=(const Empty&); Empty *operator &(); const Empty* operator &() const; };
|
2. 使用析构函数来避免造成内存泄漏
2.1. 异常处理的例子:资源泄露【小动物收养保护中心】
- 收养中心每天产生一个文件,包含当天的收养个案信息
- 读取这个文件,为每个个案做适当的处理

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class ALA{ public: virtual void processAdoption() = 0; }; class Puppy: public ALA{ public: virtual void processAdoption(); }; class Kitten: public ALA{ public: virtual void processAdoption(); }; void processAdoptions(istream& dataSource){ while (dataSource){ ALA *pa = readALA(dataSource); try{ pa->processAdoption(); }catch (…){ delete pa; throw; } delete pa; } }
|
- 结构破碎:被迫重复"清理码"2次delete的pa(不符合集中式处理的想法、同时容易导致维护困难的问题)
- 集中处理?用析构函数(智能指针)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| template <class T> class auto_ptr{ public: auto_ptr(T *p=0):ptr(p) {} ~auto_ptr() { delete ptr; } T* operator->() const { return ptr;} T& operator *() const { return *ptr; } private: T* ptr; };
void processAdoptions(istream& dataSource){ while (dataSource){ auto_ptr<ALA> pa(readALA(dataSource)); pa->processAdoption(); } }
|
2.2. GUI应用软件中的某个显示信息的函数
- handle class:句柄类,就是处理智能指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| void displayInfo(const Information& info){ WINDOW_HANDLE w(createWindow()); display info in window corresponding to w; destroyWindows(w); }
class WindowHandle{ public: WindowHandle(WINDOW_HANDLE handler) : w(handler) {} ~WindowHandle() { destroyWindow(w);} operator WINDOW_HANDLE() { return w; } private: WINDOW_HANDLE w; WindowHandle(const WindowHandle&); WindowHandle & operator = (const WindowHandle&); }; void displayInfo(const Information& info){ WindowHandle w(createWindow()) }
|
- 第9、10课需要仔细听一下
评论